library(pacman)
p_load(modelr, tidyverse)

gapminder

p_load(gapminder)
gapminder

We want to answer: “how does life expectancy change over time for each country?”

ggplot(gapminder, aes(year, lifeExp, group = country)) +
  geom_line(alpha = 1/3)

To identify the countries that don’t have upward trends, we’ll fit a model with a linear trend. The model will capture steady growth over time, and the residuals will show what’s left.

nz <- filter(gapminder, country == "New Zealand")

nz %>% 
  ggplot(aes(year, lifeExp)) +
  geom_line() +
  ggtitle("Full data = ")


nz_mod <- lm(lifeExp ~ year, data = nz)
nz %>% 
  add_predictions(nz_mod) %>% 
  ggplot(aes(year, pred)) +
  geom_line() +
  ggtitle("Linear trend + ")


nz %>% 
  add_residuals(nz_mod) %>% 
  ggplot(aes(year, resid)) +
  geom_hline(yintercept = 0, color = "white", size = 3) +
  geom_line() +
  ggtitle("Remaining pattern")

How can we easily fit this to every country?

Nested data

Extract out the common code with a function and repeat using a map function from purrr. This time, instead of repeating an action for each variable, we want to repeat an action for each country, a subset of rows. For that, we need a nested data frame.

by_country <- gapminder %>% 
  group_by(country, continent) %>% 
  nest()

by_country
by_country$data[[1]]

List-columns

With the nested data frame, we’re in a good position to fit some models.

country_model <- function(df) {
  lm(lifeExp ~ year, data = df)
}

We want to apply this to every data frame.

models <- map(by_country$data, country_model)

Rather than storing the results in their own object, It’s better to store them in as a column in the by_country data frame.

Unnesting

To get the residuals, we need to call add_residuals() with each model-data pair.

Now, we can turn the list of data frames back into a regular data frame.

Now we can plot the residuals:

resids %>% 
  ggplot(aes(year, resid)) +
  geom_line(aes(group = country), alpha = 1/3) +
  geom_smooth(se = FALSE)
`geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

And facet by continent:

resids %>% 
  ggplot(aes(year, resid)) +
  geom_line(aes(group = country), alpha = 1/3) +
  geom_smooth(se = FALSE) +
  facet_wrap(~ continent)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

Model quality

We can use mutate() and unnest() to create a data frame with a row for each country:

To drop the list columns, we use .drop = TRUE in unnest():

Now we can look for models that don’t fit well:

Let’s plot out Africa since they have the worst models:

Analyzing the Data with Multilevel Models

Multilevel models are beneficial here because of the structure of the data:

  • Time influences changes in the outcome variable for each country

  • Countries have trends that influence the outcome

  • Countries are nested within continents which can have trends

  • Finally, time-based trends can be different across countries and continents too

Clean the data

p_load(ggrepel, brms, cmdstanr, tidybayes, ggh4x)
trying URL 'https://cran.rstudio.com/bin/macosx/big-sur-arm64/contrib/4.1/ggh4x_0.2.1.tgz'
Content type 'application/x-gzip' length 1932363 bytes (1.8 MB)
==================================================
downloaded 1.8 MB

The downloaded binary packages are in
    /var/folders/hm/klbdvmzd6mz_cqks_tjsp9d00000gn/T//Rtmp9RlayS/downloaded_packages

ggh4x installed

The effect of continent, country, and time on life expectancy

ggplot(gapminder_clean, aes(x = year, y = lifeExp, 
                      group = country, color = continent)) +
  geom_line(aes(size = highlight)) +
  geom_smooth(method = "lm", aes(color = NULL, group = NULL), 
              color = "grey60", size = 1, linetype = "21",
              se = FALSE, show.legend = FALSE) +
  geom_label_repel(data = filter(gapminder_clean, year_c == 0, highlight == TRUE),
                   aes(label = country), direction = "y", size = 3, seed = 1234,
                   show.legend = FALSE) +
  annotate(geom = "label", label = "Global trend", x = 1952, y = 50,
           size = 3, color = "grey60") +
  scale_size_manual(values = c(0.075, 1), guide = "none") +
  #scale_color_okabe_ito(order = c(2, 3, 6, 1)) +
  labs(x = NULL, y = "Life expectancy", color = "Continent") +
  theme_minimal() +
  theme(legend.position = "bottom")
`geom_smooth()` using formula 'y ~ x'

Regular regression

We start with a model that ignores continent and country-level differences.

pooling_fit <- lm(lifeExp ~ year_c,
                  data = gapminder_clean)

tidy(pooling_fit)

Plot the fitted regression.

model_preds <- gapminder_clean %>% 
  data_grid(country, year_c) %>% 
  add_predictions(pooling_fit) %>% 
  mutate(year = year_c + 1952) %>% 
  select(-year_c) %>% 
  left_join(gapminder_clean, by = c("country", "year"))

set.seed(1)
model_preds %>% 
  # nest all year-country obsevations within continents 
  group_by(continent, country) %>% 
  nest() %>% 
  # randomly select all years of 2 countries from each continent
  group_by(continent) %>% 
  slice_sample(n = 2) %>% 
  # expand the data
  unnest() %>% 
  # plot
  ggplot(aes(x = year, y = pred)) +
  # plot original data
  geom_point(aes(y = lifeExp)) +
  # plot regression line
  geom_line(color = "red") +
  facet_nested_wrap(vars(continent, country))
Warning: `cols` is now required when using unnest().
Please use `cols = c(data)`

Intercepts for each continent

How much does life expectancy increase on average as time passes? In the previous model, year captures the global trend, but it doesn’t account for continent or country-specific differences. We can see the failure of this omission by visualizing the residuals:

model_resids <- gapminder_clean %>% 
  add_residuals(pooling_fit)

set.seed(1)
model_resids %>% 
  # nest all year-country obsevations within continents 
  group_by(continent, country) %>% 
  nest() %>% 
  # randomly select all years of 2 countries from each continent
  group_by(continent) %>% 
  slice_sample(n = 2) %>% 
  # expand the data
  unnest() %>% 
  # plot
  ggplot(aes(x = year, y = resid)) +
  # plot residuals
  geom_line(color = "red") +
  facet_nested_wrap(vars(continent, country))
Warning: `cols` is now required when using unnest().
Please use `cols = c(data)`

This plot shows the direction of our errors. Starting around 1995 in Botswana, the model predicted a much higher life expectancy than was actually the case. In general, the negative residual values mean that the model was overly optimistic - it predicted a higher life expectancy that actually occurred. The positive residual values mean that the model was too pessimistic - it predicted a lower life expectancy that actually occurred. We actually miss the mark with pretty much every country. We can do better.

rand_int_fit <- lmer(lifeExp ~ year_c + (1 | continent),
                     data = gapminder_clean)

tidy(rand_int_fit)
Continent-level variance

The random effects show how much variation there is in life expectancy across continents:

tidy(rand_int_fit, effects = "ran_pars")
Continent-level random effects

We can see the actual offsets with the ranef() function.

ranef(rand_int_fit)
$continent
         (Intercept)
Africa    -15.042650
Americas    0.736923
Asia       -3.850567
Europe      7.971620
Oceania    10.184674

with conditional variances for “continent” 

These values are the continent-specific offsets in the intercept. We can add these to the population intercept to see the actual intercept for each continent:

coef(rand_int_fit)$continent %>% 
  as_tibble(rownames = "continent")

Intercepts for each country

Instead of having intercepts for each continent, let’s have intercepts for each country:

int_fit <- lmer(lifeExp ~ year_c + (1 | country),
                data = gapminder_clean)
tidy(int_fit)

Let’s examine the country offsets for our randomly sampled countries:

ranef(int_fit)$country %>% 
  as_tibble(rownames = "country") %>% 
  filter(country %in% pooling_sample$country)

And the country specific intercepts:

coef(int_fit)$country %>% 
  as_tibble(rownames = "country") %>% 
  filter(country %in% pooling_sample$country)

Intercepts and slopes for each county + account for year-specific differences

rand_model <- lmer(lifeExp ~ year_c + (1 | year_c) + (1 + year_c | country),
                   data = gapminder_clean)
Warning in checkConv(attr(opt, "derivs"), opt$par, ctrl = control$checkConv,  :
  Model failed to converge with max|grad| = 0.00332276 (tol = 0.002, component 1)
tidy(rand_model)
Year-level random effects
ranef(rand_model)$year_c %>% 
  as_tibble(rownames = "year") %>% 
  mutate(year = as.numeric(year) + 1952)
NA
Visualize country-specific intercepts and slopes and year-specific intercepts
rand_model_preds <- gapminder_clean %>% 
  data_grid(continent, country, year_c) %>% 
  add_predictions(rand_model) %>% 
  mutate(year = year_c + 1952) %>% 
  select(-year_c) %>% 
  inner_join(gapminder_clean, by = c("continent", "country", "year"))

set.seed(1)
rand_sample <- 
  rand_model_preds %>% 
  # nest all year-country obsevations within continents 
  group_by(continent, country) %>% 
  nest() %>% 
  # randomly select all years of 2 countries from each continent
  group_by(continent) %>% 
  slice_sample(n = 2) %>% 
  # expand the data
  unnest()
Warning: `cols` is now required when using unnest().
Please use `cols = c(data)`
# plot
ggplot(rand_sample, aes(x = year, y = pred)) +
  # plot original data
  geom_point(aes(y = lifeExp), shape = 21) +
  # plot pooling model predictions
  geom_line(data = pooling_sample, color = "red") +
  # plot countinent intercept prediction
  geom_line(data = rand_int_sample, color = "blue") +
  # plot county intercept predictions
  geom_line(data = int_sample, color = "orange") +
  geom_line(color = "black") +
  facet_nested_wrap(vars(continent, country))

How much does this improve the residuals:

rand_model_resids <- gapminder_clean %>% 
  add_residuals(rand_model)

set.seed(1)
rand_resids_sample <- 
  rand_model_resids %>% 
  # nest all year-country obsevations within continents 
  group_by(continent, country) %>% 
  nest() %>% 
  # randomly select all years of 2 countries from each continent
  group_by(continent) %>% 
  slice_sample(n = 2) %>% 
  # expand the data
  unnest()
Warning: `cols` is now required when using unnest().
Please use `cols = c(data)`
# plot
ggplot(rand_resids_sample, aes(x = year, y = resid)) +
  # plot pooling model residuals
  geom_line(data = pooling_resids_sample, color = "red") +
  # plot continent intercept residuals
  geom_line(data = rand_int_resids_sample, color = "blue") +
  # plot country intercept residuals
  geom_line(data = int_resids_sample, color = "orange") +
  geom_line(color = "black") +
  facet_nested_wrap(vars(continent, country))

Full model

full_fit <- lmer(lifeExp ~ year_c + I(year_c)^2 + (1 + year_c + I(year_c)^2 | continent / country),
                 data = gapminder_clean)
fixed-effect model matrix is rank deficient so dropping 1 column / coefficient
Warning in checkConv(attr(opt, "derivs"), opt$par, ctrl = control$checkConv,  :
  unable to evaluate scaled gradient
Warning in checkConv(attr(opt, "derivs"), opt$par, ctrl = control$checkConv,  :
  Model failed to converge: degenerate  Hessian with 4 negative eigenvalues
tidy(full_fit)

full_model_preds <- gapminder_clean %>% 
  add_predictions(full_fit) %>% 
  mutate(year = year_c + 1952)

set.seed(1)
full_model_sample <- 
  full_model_preds %>% 
  # nest all year-country obsevations within continents 
  group_by(continent, country) %>% 
  nest() %>% 
  # randomly select all years of 2 countries from each continent
  group_by(continent) %>% 
  slice_sample(n = 2) %>% 
  # expand the data
  unnest()
Warning: `cols` is now required when using unnest().
Please use `cols = c(data)`
# plot
ggplot(full_model_sample, aes(x = year, y = pred)) +
  # plot original data
  geom_point(aes(y = lifeExp), shape = 21) +
  # plot pooling model predictions
  geom_line(data = pooling_sample, color = "red") +
  # plot countinent intercept prediction
  geom_line(data = rand_int_sample, color = "blue") +
  # plot county intercept predictions
  geom_line(data = int_sample, color = "orange") +
  geom_line(data = rand_sample, color = "black") +
  geom_line(color = "green") +
  facet_nested_wrap(vars(continent, country))


full_model_resids <- gapminder_clean %>% 
  add_residuals(full_fit)

set.seed(1)
full_resids_sample <- 
  full_model_resids %>% 
  # nest all year-country obsevations within continents 
  group_by(continent, country) %>% 
  nest() %>% 
  # randomly select all years of 2 countries from each continent
  group_by(continent) %>% 
  slice_sample(n = 2) %>% 
  # expand the data
  unnest()
Warning: `cols` is now required when using unnest().
Please use `cols = c(data)`
# plot
ggplot(full_resids_sample, aes(x = year, y = resid)) +
  # plot pooling model residuals
  geom_line(data = pooling_resids_sample, color = "red") +
  # plot continent intercept residuals
  geom_line(data = rand_int_resids_sample, color = "blue") +
  # plot country intercept residuals
  geom_line(data = int_resids_sample, color = "orange") +
  geom_line(data = rand_resids_sample, color = "black") +
  geom_line(color = "green") +
  facet_nested_wrap(vars(continent, country))

List-columns

Creating list-columns

Specifying list-columns

LS0tCnRpdGxlOiAiQ2hhcHRlciAyNTogTWFueSBNb2RlbHMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyfQpsaWJyYXJ5KHBhY21hbikKcF9sb2FkKG1vZGVsciwgdGlkeXZlcnNlKQpgYGAKCiMjIGdhcG1pbmRlcgoKYGBge3J9CnBfbG9hZChnYXBtaW5kZXIpCmdhcG1pbmRlcgpgYGAKCldlIHdhbnQgdG8gYW5zd2VyOiAiaG93IGRvZXMgbGlmZSBleHBlY3RhbmN5IGNoYW5nZSBvdmVyIHRpbWUgZm9yIGVhY2ggY291bnRyeT8iCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHllYXIsIGxpZmVFeHAsIGdyb3VwID0gY291bnRyeSkpICsKICBnZW9tX2xpbmUoYWxwaGEgPSAxLzMpCmBgYAoKVG8gaWRlbnRpZnkgdGhlIGNvdW50cmllcyB0aGF0IGRvbid0IGhhdmUgdXB3YXJkIHRyZW5kcywgd2UnbGwgZml0IGEgbW9kZWwgd2l0aCBhIGxpbmVhciB0cmVuZC4gVGhlIG1vZGVsIHdpbGwgY2FwdHVyZSBzdGVhZHkgZ3Jvd3RoIG92ZXIgdGltZSwgYW5kIHRoZSByZXNpZHVhbHMgd2lsbCBzaG93IHdoYXQncyBsZWZ0LgoKYGBge3J9Cm56IDwtIGZpbHRlcihnYXBtaW5kZXIsIGNvdW50cnkgPT0gIk5ldyBaZWFsYW5kIikKCm56ICU+JSAKICBnZ3Bsb3QoYWVzKHllYXIsIGxpZmVFeHApKSArCiAgZ2VvbV9saW5lKCkgKwogIGdndGl0bGUoIkZ1bGwgZGF0YSA9ICIpCgpuel9tb2QgPC0gbG0obGlmZUV4cCB+IHllYXIsIGRhdGEgPSBueikKbnogJT4lIAogIGFkZF9wcmVkaWN0aW9ucyhuel9tb2QpICU+JSAKICBnZ3Bsb3QoYWVzKHllYXIsIHByZWQpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdndGl0bGUoIkxpbmVhciB0cmVuZCArICIpCgpueiAlPiUgCiAgYWRkX3Jlc2lkdWFscyhuel9tb2QpICU+JSAKICBnZ3Bsb3QoYWVzKHllYXIsIHJlc2lkKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDMpICsKICBnZW9tX2xpbmUoKSArCiAgZ2d0aXRsZSgiUmVtYWluaW5nIHBhdHRlcm4iKQpgYGAKCkhvdyBjYW4gd2UgZWFzaWx5IGZpdCB0aGlzIHRvIGV2ZXJ5IGNvdW50cnk/CgojIyMgTmVzdGVkIGRhdGEKCkV4dHJhY3Qgb3V0IHRoZSBjb21tb24gY29kZSB3aXRoIGEgZnVuY3Rpb24gYW5kIHJlcGVhdCB1c2luZyBhIG1hcCBmdW5jdGlvbiBmcm9tIHB1cnJyLiBUaGlzIHRpbWUsIGluc3RlYWQgb2YgcmVwZWF0aW5nIGFuIGFjdGlvbiBmb3IgZWFjaCB2YXJpYWJsZSwgd2Ugd2FudCB0byByZXBlYXQgYW4gYWN0aW9uIGZvciBlYWNoIGNvdW50cnksIGEgc3Vic2V0IG9mIHJvd3MuIEZvciB0aGF0LCB3ZSBuZWVkIGEgbmVzdGVkIGRhdGEgZnJhbWUuCgpgYGB7cn0KYnlfY291bnRyeSA8LSBnYXBtaW5kZXIgJT4lIAogIGdyb3VwX2J5KGNvdW50cnksIGNvbnRpbmVudCkgJT4lIAogIG5lc3QoKQoKYnlfY291bnRyeQpgYGAKCmBgYHtyfQpieV9jb3VudHJ5JGRhdGFbWzFdXQpgYGAKCiMjIyBMaXN0LWNvbHVtbnMKCldpdGggdGhlIG5lc3RlZCBkYXRhIGZyYW1lLCB3ZSdyZSBpbiBhIGdvb2QgcG9zaXRpb24gdG8gZml0IHNvbWUgbW9kZWxzLgoKYGBge3J9CmNvdW50cnlfbW9kZWwgPC0gZnVuY3Rpb24oZGYpIHsKICBsbShsaWZlRXhwIH4geWVhciwgZGF0YSA9IGRmKQp9CmBgYAoKV2Ugd2FudCB0byBhcHBseSB0aGlzIHRvIGV2ZXJ5IGRhdGEgZnJhbWUuCgpgYGB7cn0KbW9kZWxzIDwtIG1hcChieV9jb3VudHJ5JGRhdGEsIGNvdW50cnlfbW9kZWwpCmBgYAoKUmF0aGVyIHRoYW4gc3RvcmluZyB0aGUgcmVzdWx0cyBpbiB0aGVpciBvd24gb2JqZWN0LCBJdCdzIGJldHRlciB0byBzdG9yZSB0aGVtIGluIGFzIGEgY29sdW1uIGluIHRoZSBgYnlfY291bnRyeWAgZGF0YSBmcmFtZS4KCmBgYHtyfQpieV9jb3VudHJ5IDwtIGJ5X2NvdW50cnkgJT4lIAogIG11dGF0ZShtb2RlbCA9IG1hcChkYXRhLCBjb3VudHJ5X21vZGVsKSkKYnlfY291bnRyeQpgYGAKCiMjIyBVbm5lc3RpbmcKClRvIGdldCB0aGUgcmVzaWR1YWxzLCB3ZSBuZWVkIHRvIGNhbGwgYGFkZF9yZXNpZHVhbHMoKWAgd2l0aCBlYWNoIG1vZGVsLWRhdGEgcGFpci4KCmBgYHtyfQpieV9jb3VudHJ5IDwtIGJ5X2NvdW50cnkgJT4lIAogIG11dGF0ZShyZXNpZHMgPSBtYXAyKGRhdGEsIG1vZGVsLCBhZGRfcmVzaWR1YWxzKSkKYnlfY291bnRyeQpgYGAKCk5vdywgd2UgY2FuIHR1cm4gdGhlIGxpc3Qgb2YgZGF0YSBmcmFtZXMgYmFjayBpbnRvIGEgcmVndWxhciBkYXRhIGZyYW1lLgoKYGBge3J9CnJlc2lkcyA8LSB1bm5lc3QoYnlfY291bnRyeSwgcmVzaWRzKQpyZXNpZHMKYGBgCgpOb3cgd2UgY2FuIHBsb3QgdGhlIHJlc2lkdWFsczoKCmBgYHtyfQpyZXNpZHMgJT4lIAogIGdncGxvdChhZXMoeWVhciwgcmVzaWQpKSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IGNvdW50cnkpLCBhbHBoYSA9IDEvMykgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpCmBgYAoKQW5kIGZhY2V0IGJ5IGNvbnRpbmVudDoKCmBgYHtyfQpyZXNpZHMgJT4lIAogIGdncGxvdChhZXMoeWVhciwgcmVzaWQpKSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IGNvdW50cnkpLCBhbHBoYSA9IDEvMykgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICsKICBmYWNldF93cmFwKH4gY29udGluZW50KQpgYGAKCiMjIyBNb2RlbCBxdWFsaXR5CgpgYGB7cn0KcF9sb2FkKGJyb29tKQpicm9vbTo6Z2xhbmNlKG56X21vZCkKYGBgCgpXZSBjYW4gdXNlIGBtdXRhdGUoKWAgYW5kIGB1bm5lc3QoKWAgdG8gY3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIGEgcm93IGZvciBlYWNoIGNvdW50cnk6CgpgYGB7cn0KYnlfY291bnRyeSAlPiUgCiAgbXV0YXRlKGdsYW5jZSA9IG1hcChtb2RlbCwgZ2xhbmNlKSkgJT4lIAogIHVubmVzdChnbGFuY2UpCmBgYAoKVG8gZHJvcCB0aGUgbGlzdCBjb2x1bW5zLCB3ZSB1c2UgYC5kcm9wID0gVFJVRWAgaW4gYHVubmVzdCgpYDoKCmBgYHtyfQpnbGFuY2UgPC0gYnlfY291bnRyeSAlPiUgCiAgbXV0YXRlKGdsYW5jZSA9IG1hcChtb2RlbCwgZ2xhbmNlKSkgJT4lIAogIHVubmVzdChnbGFuY2UsIC5kcm9wID0gVFJVRSkKZ2xhbmNlCmBgYAoKTm93IHdlIGNhbiBsb29rIGZvciBtb2RlbHMgdGhhdCBkb24ndCBmaXQgd2VsbDoKCmBgYHtyfQpnbGFuY2UgJT4lIAogIGFycmFuZ2Uoci5zcXVhcmVkKQpgYGAKCkxldCdzIHBsb3Qgb3V0IEFmcmljYSBzaW5jZSB0aGV5IGhhdmUgdGhlIHdvcnN0IG1vZGVsczoKCmBgYHtyfQpnbGFuY2UgJT4lIAogIGdncGxvdChhZXMoY29udGluZW50LCByLnNxdWFyZWQpKSArCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjUpCmBgYAoKIyMjIEFuYWx5emluZyB0aGUgRGF0YSB3aXRoIE11bHRpbGV2ZWwgTW9kZWxzCgpNdWx0aWxldmVsIG1vZGVscyBhcmUgYmVuZWZpY2lhbCBoZXJlIGJlY2F1c2Ugb2YgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YToKCi0gICBUaW1lIGluZmx1ZW5jZXMgY2hhbmdlcyBpbiB0aGUgb3V0Y29tZSB2YXJpYWJsZSBmb3IgZWFjaCBjb3VudHJ5CgotICAgQ291bnRyaWVzIGhhdmUgdHJlbmRzIHRoYXQgaW5mbHVlbmNlIHRoZSBvdXRjb21lCgotICAgQ291bnRyaWVzIGFyZSBuZXN0ZWQgd2l0aGluIGNvbnRpbmVudHMgd2hpY2ggY2FuIGhhdmUgdHJlbmRzCgotICAgRmluYWxseSwgdGltZS1iYXNlZCB0cmVuZHMgY2FuIGJlIGRpZmZlcmVudCBhY3Jvc3MgY291bnRyaWVzIGFuZCBjb250aW5lbnRzIHRvbwoKIyMjIyBDbGVhbiB0aGUgZGF0YQoKYGBge3J9CmxpYnJhcnkocGFjbWFuKQpwX2xvYWQoZ2dyZXBlbCwgYnJtcywgY21kc3RhbnIsIHRpZHliYXllcywgZ2doNHgsIGxtZTQsIGVtbWVhbnMpCgojIGNlbnRlciB5ZWFyIG9uIHRoZSBmaXJzdCB5ZWFyIG9mIGRhdGEgKDE5NTIpCmdhcG1pbmRlcl9jbGVhbiA8LSBnYXBtaW5kZXIgJT4lIAogIG11dGF0ZSh5ZWFyX2MgPSB5ZWFyIC0gMTk1MikKCiMgZXhhbXBsZSBjb3VudHJpZXMKY291bnRyaWVzIDwtIHRyaWJibGUoCiAgfmNvdW50cnksICAgICAgIH5jb250aW5lbnQsCiAgIkVneXB0IiwgICAgICAgICJBZnJpY2EiLAogICJTaWVycmEgTGVvbmUiLCAiQWZyaWNhIiwKICAiUGFraXN0YW4iLCAgICAgIkFzaWEiLAogICJZZW1lbiwgUmVwLiIsICAiQXNpYSIsCiAgIkJvbGl2aWEiLCAgICAgICJBbWVyaWNhcyIsCiAgIkNhbmFkYSIsICAgICAgICJBbWVyaWNhcyIsCiAgIkl0YWx5IiwgICAgICAgICJFdXJvcGUiLAogICJQb3J0dWdhbCIsICAgICAiRXVyb3BlIgopCgpnYXBtaW5kZXJfY2xlYW4gPC0gZ2FwbWluZGVyX2NsZWFuICU+JSAKICBtdXRhdGUoaGlnaGxpZ2h0ID0gY291bnRyeSAlaW4lIGNvdW50cmllcyRjb3VudHJ5KQoKZ2xpbXBzZShnYXBtaW5kZXJfY2xlYW4pCmBgYAoKIyMjIyBUaGUgZWZmZWN0IG9mIGNvbnRpbmVudCwgY291bnRyeSwgYW5kIHRpbWUgb24gbGlmZSBleHBlY3RhbmN5CgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlcl9jbGVhbiwgYWVzKHggPSB5ZWFyLCB5ID0gbGlmZUV4cCwgCiAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IGNvdW50cnksIGNvbG9yID0gY29udGluZW50KSkgKwogIGdlb21fbGluZShhZXMoc2l6ZSA9IGhpZ2hsaWdodCkpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBhZXMoY29sb3IgPSBOVUxMLCBncm91cCA9IE5VTEwpLCAKICAgICAgICAgICAgICBjb2xvciA9ICJncmV5NjAiLCBzaXplID0gMSwgbGluZXR5cGUgPSAiMjEiLAogICAgICAgICAgICAgIHNlID0gRkFMU0UsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPSBmaWx0ZXIoZ2FwbWluZGVyX2NsZWFuLCB5ZWFyX2MgPT0gMCwgaGlnaGxpZ2h0ID09IFRSVUUpLAogICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gY291bnRyeSksIGRpcmVjdGlvbiA9ICJ5Iiwgc2l6ZSA9IDMsIHNlZWQgPSAxMjM0LAogICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGFubm90YXRlKGdlb20gPSAibGFiZWwiLCBsYWJlbCA9ICJHbG9iYWwgdHJlbmQiLCB4ID0gMTk1MiwgeSA9IDUwLAogICAgICAgICAgIHNpemUgPSAzLCBjb2xvciA9ICJncmV5NjAiKSArCiAgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzID0gYygwLjA3NSwgMSksIGd1aWRlID0gIm5vbmUiKSArCiAgI3NjYWxlX2NvbG9yX29rYWJlX2l0byhvcmRlciA9IGMoMiwgMywgNiwgMSkpICsKICBsYWJzKHggPSBOVUxMLCB5ID0gIkxpZmUgZXhwZWN0YW5jeSIsIGNvbG9yID0gIkNvbnRpbmVudCIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpgYGAKCiMjIyMgUmVndWxhciByZWdyZXNzaW9uCgpXZSBzdGFydCB3aXRoIGEgbW9kZWwgdGhhdCBpZ25vcmVzIGNvbnRpbmVudCBhbmQgY291bnRyeS1sZXZlbCBkaWZmZXJlbmNlcy4KCmBgYHtyfQpwb29saW5nX2ZpdCA8LSBsbShsaWZlRXhwIH4geWVhcl9jLAogICAgICAgICAgICAgICAgICBkYXRhID0gZ2FwbWluZGVyX2NsZWFuKQoKdGlkeShwb29saW5nX2ZpdCkKYGBgCgpQbG90IHRoZSBmaXR0ZWQgcmVncmVzc2lvbi4KCmBgYHtyfQpwb29saW5nX21vZGVsX3ByZWRzIDwtIGdhcG1pbmRlcl9jbGVhbiAlPiUgCiAgZGF0YV9ncmlkKGNvdW50cnksIHllYXJfYykgJT4lIAogIGFkZF9wcmVkaWN0aW9ucyhwb29saW5nX2ZpdCkgJT4lIAogIG11dGF0ZSh5ZWFyID0geWVhcl9jICsgMTk1MikgJT4lIAogIHNlbGVjdCgteWVhcl9jKSAlPiUgCiAgbGVmdF9qb2luKGdhcG1pbmRlcl9jbGVhbiwgYnkgPSBjKCJjb3VudHJ5IiwgInllYXIiKSkKCnNldC5zZWVkKDEpCnBvb2xpbmdfc2FtcGxlIDwtIAogIHBvb2xpbmdfbW9kZWxfcHJlZHMgJT4lIAogICMgbmVzdCBhbGwgeWVhci1jb3VudHJ5IG9ic2V2YXRpb25zIHdpdGhpbiBjb250aW5lbnRzIAogIGdyb3VwX2J5KGNvbnRpbmVudCwgY291bnRyeSkgJT4lIAogIG5lc3QoKSAlPiUgCiAgIyByYW5kb21seSBzZWxlY3QgYWxsIHllYXJzIG9mIDIgY291bnRyaWVzIGZyb20gZWFjaCBjb250aW5lbnQKICBncm91cF9ieShjb250aW5lbnQpICU+JSAKICBzbGljZV9zYW1wbGUobiA9IDIpICU+JSAKICAjIGV4cGFuZCB0aGUgZGF0YQogIHVubmVzdCgpCgojIHBsb3QKZ2dwbG90KHBvb2xpbmdfc2FtcGxlLCBhZXMoeCA9IHllYXIsIHkgPSBwcmVkKSkgKwogICMgcGxvdCBvcmlnaW5hbCBkYXRhCiAgZ2VvbV9wb2ludChhZXMoeSA9IGxpZmVFeHApLCBzaGFwZSA9IDIxKSArCiAgIyBwbG90IHJlZ3Jlc3Npb24gbGluZQogIGdlb21fbGluZShjb2xvciA9ICJyZWQiKSArCiAgZmFjZXRfbmVzdGVkX3dyYXAodmFycyhjb250aW5lbnQsIGNvdW50cnkpKQpgYGAKCiMjIyMgSW50ZXJjZXB0cyBmb3IgZWFjaCBjb250aW5lbnQKCkhvdyBtdWNoIGRvZXMgbGlmZSBleHBlY3RhbmN5IGluY3JlYXNlIG9uIGF2ZXJhZ2UgYXMgdGltZSBwYXNzZXM/IEluIHRoZSBwcmV2aW91cyBtb2RlbCwgYHllYXJgIGNhcHR1cmVzIHRoZSBnbG9iYWwgdHJlbmQsIGJ1dCBpdCBkb2Vzbid0IGFjY291bnQgZm9yIGNvbnRpbmVudCBvciBjb3VudHJ5LXNwZWNpZmljIGRpZmZlcmVuY2VzLiBXZSBjYW4gc2VlIHRoZSBmYWlsdXJlIG9mIHRoaXMgb21pc3Npb24gYnkgdmlzdWFsaXppbmcgdGhlIHJlc2lkdWFsczoKCmBgYHtyfQpwb29saW5nX21vZGVsX3Jlc2lkcyA8LSBnYXBtaW5kZXJfY2xlYW4gJT4lIAogIGFkZF9yZXNpZHVhbHMocG9vbGluZ19maXQpCgpzZXQuc2VlZCgxKQpwb29saW5nX3Jlc2lkc19zYW1wbGUgPC0gCiAgcG9vbGluZ19tb2RlbF9yZXNpZHMgJT4lIAogICMgbmVzdCBhbGwgeWVhci1jb3VudHJ5IG9ic2V2YXRpb25zIHdpdGhpbiBjb250aW5lbnRzIAogIGdyb3VwX2J5KGNvbnRpbmVudCwgY291bnRyeSkgJT4lIAogIG5lc3QoKSAlPiUgCiAgIyByYW5kb21seSBzZWxlY3QgYWxsIHllYXJzIG9mIDIgY291bnRyaWVzIGZyb20gZWFjaCBjb250aW5lbnQKICBncm91cF9ieShjb250aW5lbnQpICU+JSAKICBzbGljZV9zYW1wbGUobiA9IDIpICU+JSAKICAjIGV4cGFuZCB0aGUgZGF0YQogIHVubmVzdCgpCgojIHBsb3QKZ2dwbG90KHBvb2xpbmdfcmVzaWRzX3NhbXBsZSwgYWVzKHggPSB5ZWFyLCB5ID0gcmVzaWQpKSArCiAgIyBwbG90IHJlc2lkdWFscwogIGdlb21fbGluZShjb2xvciA9ICJyZWQiKSArCiAgZmFjZXRfbmVzdGVkX3dyYXAodmFycyhjb250aW5lbnQsIGNvdW50cnkpKQpgYGAKClRoaXMgcGxvdCBzaG93cyB0aGUgZGlyZWN0aW9uIG9mIG91ciBlcnJvcnMuIFN0YXJ0aW5nIGFyb3VuZCAxOTk1IGluIEJvdHN3YW5hLCB0aGUgbW9kZWwgcHJlZGljdGVkIGEgbXVjaCBoaWdoZXIgbGlmZSBleHBlY3RhbmN5IHRoYW4gd2FzIGFjdHVhbGx5IHRoZSBjYXNlLiBJbiBnZW5lcmFsLCB0aGUgbmVnYXRpdmUgcmVzaWR1YWwgdmFsdWVzIG1lYW4gdGhhdCB0aGUgbW9kZWwgd2FzIG92ZXJseSBvcHRpbWlzdGljIC0gaXQgcHJlZGljdGVkIGEgaGlnaGVyIGxpZmUgZXhwZWN0YW5jeSB0aGF0IGFjdHVhbGx5IG9jY3VycmVkLiBUaGUgcG9zaXRpdmUgcmVzaWR1YWwgdmFsdWVzIG1lYW4gdGhhdCB0aGUgbW9kZWwgd2FzIHRvbyBwZXNzaW1pc3RpYyAtIGl0IHByZWRpY3RlZCBhIGxvd2VyIGxpZmUgZXhwZWN0YW5jeSB0aGF0IGFjdHVhbGx5IG9jY3VycmVkLiBXZSBhY3R1YWxseSBtaXNzIHRoZSBtYXJrIHdpdGggcHJldHR5IG11Y2ggZXZlcnkgY291bnRyeS4gV2UgY2FuIGRvIGJldHRlci4KCmBgYHtyfQpyYW5kX2ludF9maXQgPC0gbG1lcihsaWZlRXhwIH4geWVhcl9jICsgKDEgfCBjb250aW5lbnQpLAogICAgICAgICAgICAgICAgICAgICBkYXRhID0gZ2FwbWluZGVyX2NsZWFuKQoKdGlkeShyYW5kX2ludF9maXQpCmBgYAoKIyMjIyMgQ29udGluZW50LWxldmVsIHZhcmlhbmNlCgpUaGUgcmFuZG9tIGVmZmVjdHMgc2hvdyBob3cgbXVjaCB2YXJpYXRpb24gdGhlcmUgaXMgaW4gbGlmZSBleHBlY3RhbmN5IGFjcm9zcyBjb250aW5lbnRzOgoKYGBge3J9CnRpZHkocmFuZF9pbnRfZml0LCBlZmZlY3RzID0gInJhbl9wYXJzIikKYGBgCgojIyMjIyBDb250aW5lbnQtbGV2ZWwgcmFuZG9tIGVmZmVjdHMKCldlIGNhbiBzZWUgdGhlIGFjdHVhbCBvZmZzZXRzIHdpdGggdGhlIGByYW5lZigpYCBmdW5jdGlvbi4KCmBgYHtyfQpjb250aW5lbnRfb2Zmc2V0cyA8LSByYW5lZihyYW5kX2ludF9maXQpJGNvbnRpbmVudCAlPiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gImNvbnRpbmVudCIpCmNvbnRpbmVudF9vZmZzZXRzCmBgYAoKVGhlc2UgdmFsdWVzIGFyZSB0aGUgY29udGluZW50LXNwZWNpZmljIG9mZnNldHMgaW4gdGhlIGludGVyY2VwdC4gV2UgY2FuIGFkZCB0aGVzZSB0byB0aGUgcG9wdWxhdGlvbiBpbnRlcmNlcHQgdG8gc2VlIHRoZSBhY3R1YWwgaW50ZXJjZXB0IGZvciBlYWNoIGNvbnRpbmVudDoKCmBgYHtyfQpjb2VmKHJhbmRfaW50X2ZpdCkkY29udGluZW50ICU+JSAKICBhc190aWJibGUocm93bmFtZXMgPSAiY29udGluZW50IikKYGBgCgojIyMjIyBWaXN1YWxpemUgY29udGluZW50LXNwZWNpZmljIHRyZW5kcwoKYGBge3J9CnJhbmRfaW50X21vZGVsX3ByZWRzIDwtIGdhcG1pbmRlcl9jbGVhbiAlPiUgCiAgZGF0YV9ncmlkKGNvbnRpbmVudCwgY291bnRyeSwgeWVhcl9jKSAlPiUgCiAgYWRkX3ByZWRpY3Rpb25zKHJhbmRfaW50X2ZpdCkgJT4lIAogIG11dGF0ZSh5ZWFyID0geWVhcl9jICsgMTk1MikgJT4lIAogIHNlbGVjdCgteWVhcl9jKSAlPiUgCiAgaW5uZXJfam9pbihnYXBtaW5kZXJfY2xlYW4sIGJ5ID0gYygiY29udGluZW50IiwgImNvdW50cnkiLCAieWVhciIpKQoKc2V0LnNlZWQoMSkKcmFuZF9pbnRfc2FtcGxlIDwtIAogIHJhbmRfaW50X21vZGVsX3ByZWRzICU+JSAKICAjIG5lc3QgYWxsIHllYXItY291bnRyeSBvYnNldmF0aW9ucyB3aXRoaW4gY29udGluZW50cyAKICBncm91cF9ieShjb250aW5lbnQsIGNvdW50cnkpICU+JSAKICBuZXN0KCkgJT4lIAogICMgcmFuZG9tbHkgc2VsZWN0IGFsbCB5ZWFycyBvZiAyIGNvdW50cmllcyBmcm9tIGVhY2ggY29udGluZW50CiAgZ3JvdXBfYnkoY29udGluZW50KSAlPiUgCiAgc2xpY2Vfc2FtcGxlKG4gPSAyKSAlPiUgCiAgIyBleHBhbmQgdGhlIGRhdGEKICB1bm5lc3QoKQoKIyBwbG90CmdncGxvdChyYW5kX2ludF9zYW1wbGUsIGFlcyh4ID0geWVhciwgeSA9IHByZWQpKSArCiAgIyBwbG90IG9yaWdpbmFsIGRhdGEKICBnZW9tX3BvaW50KGFlcyh5ID0gbGlmZUV4cCksIHNoYXBlID0gMjEpICsKICAjIHBsb3QgcG9vbGluZyBtb2RlbCBwcmVkaWN0aW9ucwogIGdlb21fbGluZShkYXRhID0gcG9vbGluZ19zYW1wbGUsIGNvbG9yID0gInJlZCIpICsKICAjIHBsb3QgcmVncmVzc2lvbiBsaW5lCiAgZ2VvbV9saW5lKGNvbG9yID0gImJsdWUiKSArCiAgZmFjZXRfbmVzdGVkX3dyYXAodmFycyhjb250aW5lbnQsIGNvdW50cnkpKQpgYGAKCkhvdyBtdWNoIGRpZCB0aGUgY29udGluZW50IGludGVyY2VwdCBtb2RlbCBpbXByb3ZlIHRoZSByZXNpZHVhbHM/CgpgYGB7cn0KcmFuZF9pbnRfbW9kZWxfcmVzaWRzIDwtIGdhcG1pbmRlcl9jbGVhbiAlPiUgCiAgYWRkX3Jlc2lkdWFscyhyYW5kX2ludF9maXQpCgpzZXQuc2VlZCgxKQpyYW5kX2ludF9yZXNpZHNfc2FtcGxlIDwtIAogIHJhbmRfaW50X21vZGVsX3Jlc2lkcyAlPiUgCiAgIyBuZXN0IGFsbCB5ZWFyLWNvdW50cnkgb2JzZXZhdGlvbnMgd2l0aGluIGNvbnRpbmVudHMgCiAgZ3JvdXBfYnkoY29udGluZW50LCBjb3VudHJ5KSAlPiUgCiAgbmVzdCgpICU+JSAKICAjIHJhbmRvbWx5IHNlbGVjdCBhbGwgeWVhcnMgb2YgMiBjb3VudHJpZXMgZnJvbSBlYWNoIGNvbnRpbmVudAogIGdyb3VwX2J5KGNvbnRpbmVudCkgJT4lIAogIHNsaWNlX3NhbXBsZShuID0gMikgJT4lIAogICMgZXhwYW5kIHRoZSBkYXRhCiAgdW5uZXN0KCkKCiMgcGxvdApnZ3Bsb3QocmFuZF9pbnRfcmVzaWRzX3NhbXBsZSwgYWVzKHggPSB5ZWFyLCB5ID0gcmVzaWQpKSArCiAgIyBwbG90IHBvb2xpbmcgbW9kZWwgcmVzaWR1YWxzCiAgZ2VvbV9saW5lKGRhdGEgPSBwb29saW5nX3Jlc2lkc19zYW1wbGUsIGNvbG9yID0gInJlZCIpICsKICAjIHBsb3QgcmFuZG9tIGludGVyY2VwdCByZXNpZHVhbHMKICBnZW9tX2xpbmUoY29sb3IgPSAiYmx1ZSIpICsKICBmYWNldF9uZXN0ZWRfd3JhcCh2YXJzKGNvbnRpbmVudCwgY291bnRyeSkpCmBgYAoKIyMjIyBJbnRlcmNlcHRzIGZvciBlYWNoIGNvdW50cnkKCkluc3RlYWQgb2YgaGF2aW5nIGludGVyY2VwdHMgZm9yIGVhY2ggY29udGluZW50LCBsZXQncyBoYXZlIGludGVyY2VwdHMgZm9yIGVhY2ggY291bnRyeToKCmBgYHtyfQppbnRfZml0IDwtIGxtZXIobGlmZUV4cCB+IHllYXJfYyArICgxIHwgY291bnRyeSksCiAgICAgICAgICAgICAgICBkYXRhID0gZ2FwbWluZGVyX2NsZWFuKQp0aWR5KGludF9maXQpCmBgYAoKTGV0J3MgZXhhbWluZSB0aGUgY291bnRyeSBvZmZzZXRzIGZvciBvdXIgcmFuZG9tbHkgc2FtcGxlZCBjb3VudHJpZXM6CgpgYGB7cn0KcmFuZWYoaW50X2ZpdCkkY291bnRyeSAlPiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gImNvdW50cnkiKSAlPiUgCiAgZmlsdGVyKGNvdW50cnkgJWluJSBwb29saW5nX3NhbXBsZSRjb3VudHJ5KQpgYGAKCkFuZCB0aGUgY291bnRyeSBzcGVjaWZpYyBpbnRlcmNlcHRzOgoKYGBge3J9CmNvZWYoaW50X2ZpdCkkY291bnRyeSAlPiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gImNvdW50cnkiKSAlPiUgCiAgZmlsdGVyKGNvdW50cnkgJWluJSBwb29saW5nX3NhbXBsZSRjb3VudHJ5KQpgYGAKCiMjIyMjIFZpc3VhbGl6ZSBjb3VudHJ5LXNwZWNpZmljIHRyZW5kcwoKYGBge3J9CmludF9tb2RlbF9wcmVkcyA8LSBnYXBtaW5kZXJfY2xlYW4gJT4lIAogIGRhdGFfZ3JpZChjb250aW5lbnQsIGNvdW50cnksIHllYXJfYykgJT4lIAogIGFkZF9wcmVkaWN0aW9ucyhpbnRfZml0KSAlPiUgCiAgbXV0YXRlKHllYXIgPSB5ZWFyX2MgKyAxOTUyKSAlPiUgCiAgc2VsZWN0KC15ZWFyX2MpICU+JSAKICBpbm5lcl9qb2luKGdhcG1pbmRlcl9jbGVhbiwgYnkgPSBjKCJjb250aW5lbnQiLCAiY291bnRyeSIsICJ5ZWFyIikpCgpzZXQuc2VlZCgxKQppbnRfc2FtcGxlIDwtIAogIGludF9tb2RlbF9wcmVkcyAlPiUgCiAgIyBuZXN0IGFsbCB5ZWFyLWNvdW50cnkgb2JzZXZhdGlvbnMgd2l0aGluIGNvbnRpbmVudHMgCiAgZ3JvdXBfYnkoY29udGluZW50LCBjb3VudHJ5KSAlPiUgCiAgbmVzdCgpICU+JSAKICAjIHJhbmRvbWx5IHNlbGVjdCBhbGwgeWVhcnMgb2YgMiBjb3VudHJpZXMgZnJvbSBlYWNoIGNvbnRpbmVudAogIGdyb3VwX2J5KGNvbnRpbmVudCkgJT4lIAogIHNsaWNlX3NhbXBsZShuID0gMikgJT4lIAogICMgZXhwYW5kIHRoZSBkYXRhCiAgdW5uZXN0KCkKCiMgcGxvdApnZ3Bsb3QoaW50X3NhbXBsZSwgYWVzKHggPSB5ZWFyLCB5ID0gcHJlZCkpICsKICAjIHBsb3Qgb3JpZ2luYWwgZGF0YQogIGdlb21fcG9pbnQoYWVzKHkgPSBsaWZlRXhwKSwgc2hhcGUgPSAyMSkgKwogICMgcGxvdCBwb29saW5nIG1vZGVsIHByZWRpY3Rpb25zCiAgZ2VvbV9saW5lKGRhdGEgPSBwb29saW5nX3NhbXBsZSwgY29sb3IgPSAicmVkIikgKwogICMgcGxvdCBjb3VudGluZW50IGludGVyY2VwdCBwcmVkaWN0aW9uCiAgZ2VvbV9saW5lKGRhdGEgPSByYW5kX2ludF9zYW1wbGUsIGNvbG9yID0gImJsdWUiKSArCiAgIyBwbG90IGNvdW50eSBpbnRlcmNlcHQgcHJlZGljdGlvbnMKICBnZW9tX2xpbmUoY29sb3IgPSAib3JhbmdlIikgKwogIGZhY2V0X25lc3RlZF93cmFwKHZhcnMoY29udGluZW50LCBjb3VudHJ5KSkKYGBgCgpMZXQncyBjaGVjayBvdXQgdGhlIHJlc2lkdWFscyBmb3IgdGhpcyBuZXcgbW9kZWw6CgpgYGB7cn0KaW50X21vZGVsX3Jlc2lkcyA8LSBnYXBtaW5kZXJfY2xlYW4gJT4lIAogIGFkZF9yZXNpZHVhbHMoaW50X2ZpdCkKCnNldC5zZWVkKDEpCmludF9yZXNpZHNfc2FtcGxlIDwtIAogIGludF9tb2RlbF9yZXNpZHMgJT4lIAogICMgbmVzdCBhbGwgeWVhci1jb3VudHJ5IG9ic2V2YXRpb25zIHdpdGhpbiBjb250aW5lbnRzIAogIGdyb3VwX2J5KGNvbnRpbmVudCwgY291bnRyeSkgJT4lIAogIG5lc3QoKSAlPiUgCiAgIyByYW5kb21seSBzZWxlY3QgYWxsIHllYXJzIG9mIDIgY291bnRyaWVzIGZyb20gZWFjaCBjb250aW5lbnQKICBncm91cF9ieShjb250aW5lbnQpICU+JSAKICBzbGljZV9zYW1wbGUobiA9IDIpICU+JSAKICAjIGV4cGFuZCB0aGUgZGF0YQogIHVubmVzdCgpCgojIHBsb3QKZ2dwbG90KGludF9yZXNpZHNfc2FtcGxlLCBhZXMoeCA9IHllYXIsIHkgPSByZXNpZCkpICsKICAjIHBsb3QgcG9vbGluZyBtb2RlbCByZXNpZHVhbHMKICBnZW9tX2xpbmUoZGF0YSA9IHBvb2xpbmdfcmVzaWRzX3NhbXBsZSwgY29sb3IgPSAicmVkIikgKwogICMgcGxvdCBjb250aW5lbnQgaW50ZXJjZXB0IHJlc2lkdWFscwogIGdlb21fbGluZShkYXRhID0gcmFuZF9pbnRfcmVzaWRzX3NhbXBsZSwgY29sb3IgPSAiYmx1ZSIpICsKICAjIHBsb3QgY291bnRyeSBpbnRlcmNlcHQgcmVzaWR1YWxzCiAgZ2VvbV9saW5lKGNvbG9yID0gIm9yYW5nZSIpICsKICBmYWNldF9uZXN0ZWRfd3JhcCh2YXJzKGNvbnRpbmVudCwgY291bnRyeSkpCmBgYAoKIyMjIyBJbnRlcmNlcHRzIGFuZCBzbG9wZXMgZm9yIGVhY2ggY291bnR5ICsgYWNjb3VudCBmb3IgeWVhci1zcGVjaWZpYyBkaWZmZXJlbmNlcwoKYGBge3J9CnJhbmRfbW9kZWwgPC0gbG1lcihsaWZlRXhwIH4geWVhcl9jICsgKDEgfCB5ZWFyX2MpICsgKDEgKyB5ZWFyX2MgfCBjb3VudHJ5KSwKICAgICAgICAgICAgICAgICAgIGRhdGEgPSBnYXBtaW5kZXJfY2xlYW4pCgp0aWR5KHJhbmRfbW9kZWwpCmBgYAoKIyMjIyMgWWVhci1sZXZlbCByYW5kb20gZWZmZWN0cwoKYGBge3J9CnJhbmVmKHJhbmRfbW9kZWwpJHllYXJfYyAlPiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gInllYXIiKSAlPiUgCiAgbXV0YXRlKHllYXIgPSBhcy5udW1lcmljKHllYXIpICsgMTk1MikKCmBgYAoKIyMjIyMgVmlzdWFsaXplIGNvdW50cnktc3BlY2lmaWMgaW50ZXJjZXB0cyBhbmQgc2xvcGVzIGFuZCB5ZWFyLXNwZWNpZmljIGludGVyY2VwdHMKCmBgYHtyfQpyYW5kX21vZGVsX3ByZWRzIDwtIGdhcG1pbmRlcl9jbGVhbiAlPiUgCiAgZGF0YV9ncmlkKGNvbnRpbmVudCwgY291bnRyeSwgeWVhcl9jKSAlPiUgCiAgYWRkX3ByZWRpY3Rpb25zKHJhbmRfbW9kZWwpICU+JSAKICBtdXRhdGUoeWVhciA9IHllYXJfYyArIDE5NTIpICU+JSAKICBzZWxlY3QoLXllYXJfYykgJT4lIAogIGlubmVyX2pvaW4oZ2FwbWluZGVyX2NsZWFuLCBieSA9IGMoImNvbnRpbmVudCIsICJjb3VudHJ5IiwgInllYXIiKSkKCnNldC5zZWVkKDEpCnJhbmRfc2FtcGxlIDwtIAogIHJhbmRfbW9kZWxfcHJlZHMgJT4lIAogICMgbmVzdCBhbGwgeWVhci1jb3VudHJ5IG9ic2V2YXRpb25zIHdpdGhpbiBjb250aW5lbnRzIAogIGdyb3VwX2J5KGNvbnRpbmVudCwgY291bnRyeSkgJT4lIAogIG5lc3QoKSAlPiUgCiAgIyByYW5kb21seSBzZWxlY3QgYWxsIHllYXJzIG9mIDIgY291bnRyaWVzIGZyb20gZWFjaCBjb250aW5lbnQKICBncm91cF9ieShjb250aW5lbnQpICU+JSAKICBzbGljZV9zYW1wbGUobiA9IDIpICU+JSAKICAjIGV4cGFuZCB0aGUgZGF0YQogIHVubmVzdCgpCgojIHBsb3QKZ2dwbG90KHJhbmRfc2FtcGxlLCBhZXMoeCA9IHllYXIsIHkgPSBwcmVkKSkgKwogICMgcGxvdCBvcmlnaW5hbCBkYXRhCiAgZ2VvbV9wb2ludChhZXMoeSA9IGxpZmVFeHApLCBzaGFwZSA9IDIxKSArCiAgIyBwbG90IHBvb2xpbmcgbW9kZWwgcHJlZGljdGlvbnMKICBnZW9tX2xpbmUoZGF0YSA9IHBvb2xpbmdfc2FtcGxlLCBjb2xvciA9ICJyZWQiKSArCiAgIyBwbG90IGNvdW50aW5lbnQgaW50ZXJjZXB0IHByZWRpY3Rpb24KICBnZW9tX2xpbmUoZGF0YSA9IHJhbmRfaW50X3NhbXBsZSwgY29sb3IgPSAiYmx1ZSIpICsKICAjIHBsb3QgY291bnR5IGludGVyY2VwdCBwcmVkaWN0aW9ucwogIGdlb21fbGluZShkYXRhID0gaW50X3NhbXBsZSwgY29sb3IgPSAib3JhbmdlIikgKwogIGdlb21fbGluZShjb2xvciA9ICJibGFjayIpICsKICBmYWNldF9uZXN0ZWRfd3JhcCh2YXJzKGNvbnRpbmVudCwgY291bnRyeSkpCmBgYAoKSG93IG11Y2ggZG9lcyB0aGlzIGltcHJvdmUgdGhlIHJlc2lkdWFsczoKCmBgYHtyfQpyYW5kX21vZGVsX3Jlc2lkcyA8LSBnYXBtaW5kZXJfY2xlYW4gJT4lIAogIGFkZF9yZXNpZHVhbHMocmFuZF9tb2RlbCkKCnNldC5zZWVkKDEpCnJhbmRfcmVzaWRzX3NhbXBsZSA8LSAKICByYW5kX21vZGVsX3Jlc2lkcyAlPiUgCiAgIyBuZXN0IGFsbCB5ZWFyLWNvdW50cnkgb2JzZXZhdGlvbnMgd2l0aGluIGNvbnRpbmVudHMgCiAgZ3JvdXBfYnkoY29udGluZW50LCBjb3VudHJ5KSAlPiUgCiAgbmVzdCgpICU+JSAKICAjIHJhbmRvbWx5IHNlbGVjdCBhbGwgeWVhcnMgb2YgMiBjb3VudHJpZXMgZnJvbSBlYWNoIGNvbnRpbmVudAogIGdyb3VwX2J5KGNvbnRpbmVudCkgJT4lIAogIHNsaWNlX3NhbXBsZShuID0gMikgJT4lIAogICMgZXhwYW5kIHRoZSBkYXRhCiAgdW5uZXN0KCkKCiMgcGxvdApnZ3Bsb3QocmFuZF9yZXNpZHNfc2FtcGxlLCBhZXMoeCA9IHllYXIsIHkgPSByZXNpZCkpICsKICAjIHBsb3QgcG9vbGluZyBtb2RlbCByZXNpZHVhbHMKICBnZW9tX2xpbmUoZGF0YSA9IHBvb2xpbmdfcmVzaWRzX3NhbXBsZSwgY29sb3IgPSAicmVkIikgKwogICMgcGxvdCBjb250aW5lbnQgaW50ZXJjZXB0IHJlc2lkdWFscwogIGdlb21fbGluZShkYXRhID0gcmFuZF9pbnRfcmVzaWRzX3NhbXBsZSwgY29sb3IgPSAiYmx1ZSIpICsKICAjIHBsb3QgY291bnRyeSBpbnRlcmNlcHQgcmVzaWR1YWxzCiAgZ2VvbV9saW5lKGRhdGEgPSBpbnRfcmVzaWRzX3NhbXBsZSwgY29sb3IgPSAib3JhbmdlIikgKwogIGdlb21fbGluZShjb2xvciA9ICJibGFjayIpICsKICBmYWNldF9uZXN0ZWRfd3JhcCh2YXJzKGNvbnRpbmVudCwgY291bnRyeSkpCmBgYAoKIyMjIyBGdWxsIG1vZGVsCgpgYGB7cn0KZnVsbF9maXQgPC0gbG1lcihsaWZlRXhwIH4geWVhcl9jICsgSSh5ZWFyX2MpXjIgKyAoMSArIHllYXJfYyArIEkoeWVhcl9jKV4yIHwgY29udGluZW50IC8gY291bnRyeSksCiAgICAgICAgICAgICAgICAgZGF0YSA9IGdhcG1pbmRlcl9jbGVhbikKdGlkeShmdWxsX2ZpdCkKCmZ1bGxfbW9kZWxfcHJlZHMgPC0gZ2FwbWluZGVyX2NsZWFuICU+JSAKICBhZGRfcHJlZGljdGlvbnMoZnVsbF9maXQpICU+JSAKICBtdXRhdGUoeWVhciA9IHllYXJfYyArIDE5NTIpCgpzZXQuc2VlZCgxKQpmdWxsX21vZGVsX3NhbXBsZSA8LSAKICBmdWxsX21vZGVsX3ByZWRzICU+JSAKICAjIG5lc3QgYWxsIHllYXItY291bnRyeSBvYnNldmF0aW9ucyB3aXRoaW4gY29udGluZW50cyAKICBncm91cF9ieShjb250aW5lbnQsIGNvdW50cnkpICU+JSAKICBuZXN0KCkgJT4lIAogICMgcmFuZG9tbHkgc2VsZWN0IGFsbCB5ZWFycyBvZiAyIGNvdW50cmllcyBmcm9tIGVhY2ggY29udGluZW50CiAgZ3JvdXBfYnkoY29udGluZW50KSAlPiUgCiAgc2xpY2Vfc2FtcGxlKG4gPSAyKSAlPiUgCiAgIyBleHBhbmQgdGhlIGRhdGEKICB1bm5lc3QoKQoKIyBwbG90CmdncGxvdChmdWxsX21vZGVsX3NhbXBsZSwgYWVzKHggPSB5ZWFyLCB5ID0gcHJlZCkpICsKICAjIHBsb3Qgb3JpZ2luYWwgZGF0YQogIGdlb21fcG9pbnQoYWVzKHkgPSBsaWZlRXhwKSwgc2hhcGUgPSAyMSkgKwogICMgcGxvdCBwb29saW5nIG1vZGVsIHByZWRpY3Rpb25zCiAgZ2VvbV9saW5lKGRhdGEgPSBwb29saW5nX3NhbXBsZSwgY29sb3IgPSAicmVkIikgKwogICMgcGxvdCBjb3VudGluZW50IGludGVyY2VwdCBwcmVkaWN0aW9uCiAgZ2VvbV9saW5lKGRhdGEgPSByYW5kX2ludF9zYW1wbGUsIGNvbG9yID0gImJsdWUiKSArCiAgIyBwbG90IGNvdW50eSBpbnRlcmNlcHQgcHJlZGljdGlvbnMKICBnZW9tX2xpbmUoZGF0YSA9IGludF9zYW1wbGUsIGNvbG9yID0gIm9yYW5nZSIpICsKICBnZW9tX2xpbmUoZGF0YSA9IHJhbmRfc2FtcGxlLCBjb2xvciA9ICJibGFjayIpICsKICBnZW9tX2xpbmUoY29sb3IgPSAiZ3JlZW4iKSArCiAgZmFjZXRfbmVzdGVkX3dyYXAodmFycyhjb250aW5lbnQsIGNvdW50cnkpKQoKZnVsbF9tb2RlbF9yZXNpZHMgPC0gZ2FwbWluZGVyX2NsZWFuICU+JSAKICBhZGRfcmVzaWR1YWxzKGZ1bGxfZml0KQoKc2V0LnNlZWQoMSkKZnVsbF9yZXNpZHNfc2FtcGxlIDwtIAogIGZ1bGxfbW9kZWxfcmVzaWRzICU+JSAKICAjIG5lc3QgYWxsIHllYXItY291bnRyeSBvYnNldmF0aW9ucyB3aXRoaW4gY29udGluZW50cyAKICBncm91cF9ieShjb250aW5lbnQsIGNvdW50cnkpICU+JSAKICBuZXN0KCkgJT4lIAogICMgcmFuZG9tbHkgc2VsZWN0IGFsbCB5ZWFycyBvZiAyIGNvdW50cmllcyBmcm9tIGVhY2ggY29udGluZW50CiAgZ3JvdXBfYnkoY29udGluZW50KSAlPiUgCiAgc2xpY2Vfc2FtcGxlKG4gPSAyKSAlPiUgCiAgIyBleHBhbmQgdGhlIGRhdGEKICB1bm5lc3QoKQoKIyBwbG90CmdncGxvdChmdWxsX3Jlc2lkc19zYW1wbGUsIGFlcyh4ID0geWVhciwgeSA9IHJlc2lkKSkgKwogICMgcGxvdCBwb29saW5nIG1vZGVsIHJlc2lkdWFscwogIGdlb21fbGluZShkYXRhID0gcG9vbGluZ19yZXNpZHNfc2FtcGxlLCBjb2xvciA9ICJyZWQiKSArCiAgIyBwbG90IGNvbnRpbmVudCBpbnRlcmNlcHQgcmVzaWR1YWxzCiAgZ2VvbV9saW5lKGRhdGEgPSByYW5kX2ludF9yZXNpZHNfc2FtcGxlLCBjb2xvciA9ICJibHVlIikgKwogICMgcGxvdCBjb3VudHJ5IGludGVyY2VwdCByZXNpZHVhbHMKICBnZW9tX2xpbmUoZGF0YSA9IGludF9yZXNpZHNfc2FtcGxlLCBjb2xvciA9ICJvcmFuZ2UiKSArCiAgZ2VvbV9saW5lKGRhdGEgPSByYW5kX3Jlc2lkc19zYW1wbGUsIGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fbGluZShjb2xvciA9ICJncmVlbiIpICsKICBmYWNldF9uZXN0ZWRfd3JhcCh2YXJzKGNvbnRpbmVudCwgY291bnRyeSkpCmBgYAoKIyMgTGlzdC1jb2x1bW5zCgojIyBDcmVhdGluZyBsaXN0LWNvbHVtbnMKCiMjIFNwZWNpZnlpbmcgbGlzdC1jb2x1bW5zCg==